#!/usr/bin/python
# -*- coding: utf-8 -*-

import simplejson
import MuConnection
import time
from StringIO import StringIO
import logging

log = logging.getLogger('MuPython.MuScaleApi')

"""
My rough scale notes to be replaced with better comments later

JSON objects
{
"musl":"scenario(name: \"http_get\") {}",
"csv":"",
"hosts":{"host_0":"b1/8.1.1.1","host_1":"b2/8.1.1.2"},
"delay":0,
"pattern":{"iterations":1,"intervals":[{"iterations":1,"end":100000,
           "start":5000,"duration":60}]}
}
"""


class MuScaleApi:

    VERBOSE = False
    muConnection = None
    json = None

    def __init__(self, connection):
        """ set up the TestAPI with the passed in MuConnection """

        self.muConnection = connection

    def newSession(self):
        """ start a new session 
        HTTP POST  https://MyMu/api/v5/scale/new/
        """
        url = self.muConnection.TOP_LEVEL_URL + "/api/v5/scale/new/"
        responseData = self.muConnection.post(url, None, "text/xml",
                True)
        self.session = responseData
        if responseData.isCompleted() and not responseData.hasError():
            if responseData.getRawResponse() == '':
                responseData.setUUID('')
                responseData.setMessage('No UUID Found. No player available.')
                responseData.setCompleted(False)
                responseData.setError(True)
            else:
                responseData.setUUID(responseData.getRawResponse())
        log.debug(responseData)
        log.debug("newSession")
        return responseData

    def listActiveScalePlayers(self):
        """ List active players
        HTTP GET  https://MyMu/api/v5/scale/list
        """
        url = self.muConnection.TOP_LEVEL_URL + '/api/v5/scale/list'
        responseData = self.muConnection.get(url)
        self.session = responseData
        if responseData.isCompleted() and not responseData.hasError():
            tmpS = responseData.getRawResponse().split('"')
            if len(tmpS) == 3:
                responseData.setUUID(tmpS[1])
            else:
                responseData.setUUID('')
                responseData.setMessage('No UUID Found')
        log.debug(responseData)
        log.debug("listActiveScalePlayers")
        return responseData

    def initScalePlayer(self, scaleConfig, player_uuid):
        """ set up test and verrify: JSON object
        HTTP POST  https://MyMu/api/v5/scale/verify
        Should return a success
        """
        scaleJsonData = str(scaleConfig)
        self.json = scaleJsonData

        url = self.muConnection.TOP_LEVEL_URL + '/api/v5/scale/verify'
        req = self.muConnection.getRequest('POST', url, scaleJsonData)
        req.get_method = lambda : 'POST'
        req.add_header('Content-Type', "text/xml; charset=UTF-8")
        req.add_header("User-Agent", self.muConnection.USERNAME)
        req.add_header("Keep-Alive", 115)
        req.add_header("Cookie", "uuid=" + player_uuid)
        responseData = self.muConnection.executeRequest(req, True)
        responseData.setRequest(req)
        responseData.setUrl(url)
        if responseData.isCompleted() and not responseData.hasError():
            if responseData.getRetrievedObjectType() == responseData.SCALE_STATS:
                status = Status(responseData.getRetrievedObject())
                responseData.setRetrievedObject(status)
            else:
                log.debug('NO STATS FOUND during scale player init')
                responseData.setMessage('NO STATS FOUND')
                responseData.setCompleted(False)
                responseData.setError(True)
        self.session = responseData
        log.debug(responseData)
        log.debug("inintScalePlayer")
        return responseData

    def startScalePlayer(self, scaleConfig, player_uuid):
        """ Then kick it off with start
        HTTP POST  https://MyMu/api/v5/scale/start
        (mime-type = application/json, post content = params)
        """
        url = self.muConnection.TOP_LEVEL_URL + \
            '/api/v5/scale/start?uuid=' + player_uuid

        scaleConfig = self.json

        req = self.muConnection.getRequest('POST', url, scaleConfig)
        req.get_method = lambda : 'POST'
        req.add_header('Content-Type', "application/json; charset=utf-8")
        req.add_header("User-Agent", self.muConnection.USERNAME)
        req.add_header("Keep-Alive", 115)
        req.add_header("Cookie", "uuid=" + player_uuid)
        responseData = self.muConnection.executeRequest(req, True)
        if responseData.getRawResponse() == "{}":
            responseData.set
        self.session = responseData
        log.debug(responseData)
        log.debug("startScalePlayer")
        return responseData

    def getScalePlayerStatus(self, uuid):
        """ Monitor status
        HTTP GET  https://MyMu/api/v5/scale/status?uuid=<session_uuid>
        returns detaild json object
        """
        url = self.muConnection.TOP_LEVEL_URL + \
            '/api/v5/scale/status?uuid=' + uuid
        responseData = self.muConnection.get(url, True)
        self.session = responseData
        if responseData.isCompleted() and not responseData.hasError():
            if responseData.getRetrievedObjectType() == responseData.SCALE_STATS:
                status = Status(responseData.getRetrievedObject())
                responseData.setRetrievedObject(status)
            else:
                responseData.setMessage('NO STATS FOUND')
                responseData.setCompleted(False)
                responseData.setError(True)
        log.debug(responseData)
        log.debug("getScalePlayerStaus")
        return responseData

    def stopScalePlayer(self, player_uuid):
        """ Manualy stop test
        HTTP GET  https://MyMu/api/v5/scale/stop
        """
        url = self.muConnection.TOP_LEVEL_URL + '/api/v5/scale/stop'
        req = self.muConnection.getRequest('GET', url, None)
        req.add_header('Content-Type', "application/json; charset=utf-8")
        req.add_header("User-Agent", self.muConnection.USERNAME)
        req.add_header("Keep-Alive", 115)
        req.add_header("Cookie", "uuid=" + player_uuid)
        responseData = self.muConnection.executeRequest(req, True)
        self.session = responseData
        log.debug(responseData)
        log.debug("stopScalePlayer")
        return responseData

    def updateScalePatern(self, player_uuid, volume=None, delay=None):
        """Dynamic custom patern.  SWEET!!!!!!
        Can only be used if no paterns are set in initScalePlayer!!!!
        HTTP GET  https://MyMu/api/v5/scale/update/?volume=<new_volume>
        HTTP GET  https://MyMu/api/v5/scale/update/?delay=<new_delay> 
            Does delay work????
        """
        url = self.muConnection.TOP_LEVEL_URL + '/api/v5/scale/update?'
        if not volume == None:
            url = url + 'volume=' + str(volume)
        if not delay == None:
            if not volume == None:
                url = url + '&'
            url = url + 'delay=' + str(delay)
        req = self.muConnection.getRequest('GET', url, None)
        req.add_header('Content-Type', "application/json; charset=utf-8")
        req.add_header("User-Agent", self.muConnection.USERNAME)
        req.add_header("Keep-Alive", 115)
        req.add_header("Cookie", "uuid=" + player_uuid)
        responseData = self.muConnection.executeRequest(req, True)
        self.session = responseData
        log.debug(responseData)
        log.debug("updateScalePatern")
        return responseData

    def releaseScalePlayer(self, uuid):
        """ To release the Scale Player (so another user can access it) use 
        the /release method:
        HTTP GET https://MyMu/api/v5/scale/release?uuid=<session_uuid>
        """
        url = self.muConnection.TOP_LEVEL_URL + \
            '/api/v5/scale/release?uuid=' + uuid
        responseData = self.muConnection.get(url)

        self.session = responseData
        log.debug(responseData)
        log.debug("releaseScalePlayer")
        return responseData


class scaleConfig:
    scenarioMusl = None
    delay = 0
    pattern = {}
    csv = ''
    hosts = {}
    scaleJsonConfig = {'musl': scenarioMusl, 'csv': csv, 'hosts': hosts,
                       'pattern': pattern, 'delay': delay}

    def __str__(self):
        if self.pattern == None or self.pattern == {} or self.pattern == \
            '':
            self.scaleJsonConfig = {'musl': self.scenarioMusl, 'csv': self.csv,
                                    'hosts': self.hosts}
        else:
            self.scaleJsonConfig = {'musl': self.scenarioMusl, 'csv': self.csv,
                                    'hosts': self.hosts, 'pattern': self.pattern,
                                    'delay': self.delay}
        return simplejson.dumps(self.scaleJsonConfig)

    def loadScenarioMusl(self):
        '''TODO loadScenarioMusl'''
        raise NotImplementedError

    def setScenarioMusl(self, muslData):
        self.scenarioMusl = muslData

    def setCsv(self, csvData):
        self.csv = csvData

    def getHostsRoles(self):
        """TODO eventualy used to get hostRoles to iterate through and set"""
        raise NotImplementedError

    def setHosts(self, hostRoleList):
        self.hosts = hostRoleList

    def setHostRole(self):
        """TODO used to set or change the interface and inital ip of the 
        host role"""
        raise NotImplementedError

    def setPattern(self, patternList):
        if patternList == None or patternList == {} or patternList == '':
            self.pattern = ''
        else:
            self.pattern = patternList
            self.volume = None

    def setDelay(self, delay):
        self.delay = delay

    def getScaleJsonConfig(self):
        if self.pattern == None or self.pattern == {} or self.pattern == \
            '':
            self.scaleJsonConfig = {'musl': self.scenarioMusl, 'csv': self.csv,
                                    'hosts': self.hosts}
        else:
            self.scaleJsonConfig = {'musl': self.scenarioMusl, 'csv': self.csv,
                                    'hosts': self.hosts, 'pattern': self.pattern,
                                    'delay': self.delay}
        return self.scaleJsonConfig


class Host:
    hostRole = None
    ip = None
    interface = None


class Patern:
    ''' TODO: NEED TO CREATE PATERN MANAGMENT
    {"iterations":1,
     "intervals":[{"iterations":1,"end":100,"start":1,"duration":60}]}
    '''
    TRIANGLE = {'pattern': {"iterations": 4, "intervals": [{"iterations": 1,
                "duration": 60, "start": 100, "end": 1000}, {"iterations": 1,
                "duration": 60, "start": 1000, "end": 100}]}}
    SAW_TOOTH = {'pattern': {"iterations": 4, "intervals": [{"iterations": 4,
                 "duration": 60, "start": 100, "end": 1000}]}}
    SQUARE = {'pattern': {"iterations": 4, "intervals": [{"iterations": 1,
              "duration": 60, "start": 100, "end": 100}, {"iterations": 1,
              "duration": 60, "start": 1000, "end": 1000}]}}
    RAMP_UP = {'pattern': {"iterations": 1, "intervals": [{"iterations": 1,
               "duration": 60, "start": 100, "end": 1000}]}}
    RAMP_DOWN = {'pattern': {"iterations": 1, "intervals": [{"iterations": 1,
                 "duration": 60, "start": 1000, "end": 100}]}}
    CONSTANT = {'pattern': {"iterations": 1, "intervals": [{"iterations": 1,
                "duration": 60, "start": 1000, "end": 1000}]}}
    STEP_UP = {'pattern': {"iterations": 1, "intervals": [
        {"iterations": 1, "duration": 60, "start": 100, "end": 100},
        {"iterations": 1, "duration": 60, "start": 200, "end": 200},
        {"iterations": 1, "duration": 60, "start": 300, "end": 300},
        {"iterations": 1, "duration": 60, "start": 400, "end": 400},
        {"iterations": 1, "duration": 60, "start": 500, "end": 500},
        {"iterations": 1, "duration": 60, "start": 600, "end": 600},
        {"iterations": 1, "duration": 60, "start": 700, "end": 700},
        {"iterations": 1, "duration": 60, "start": 800, "end": 800},
        {"iterations": 1, "duration": 60, "start": 900, "end": 900},
        {"iterations": 1, "duration": 60, "start": 1000, "end": 1000},
        ]}}


class Status:

    jsonData = {'statistics': {}, 'uuid': '', 'pollIndex': 0, 'running': False,
                'systime': 0, '_id': '', 'okay': False}
    running = False
    uuid = None
    pollIndex = None
    systime = None
    _id = None
    okay = False
    statistics = None
    volume = 0

    def __init__(self, jsonData=None):
        if jsonData == None:
            return
        for key in jsonData:
            if key == 'statistics':
                self.statistics = Statistics(jsonData[key])
                self.volume = self.statistics.volume
            elif key == 'running':
                self.running = jsonData[key]
            elif key == 'uuid':
                self.uuid = jsonData[key]
            elif key == 'pollIndex':
                self.pollIndex = jsonData[key]
            elif key == 'systime':
                self.systime = jsonData[key]
            elif key == '_id':
                self._id = jsonData[key]
            elif key == 'okay':
                self.okay = jsonData[key]
            else:
                log.info("UKNOWN STATUS KEY IN MU SCALE API------> %s", 
                    str(key))

    def __str__(self):
        return '{ running:' + str(self.running) + ', okay:' + str(self.okay) + \
            ', pollIndex:' + str(self.pollIndex) + ', volume:' + str(self.volume) + \
            ', uuid:' + str(self.uuid) + ', systime:' + str(self.systime) + \
            ', _id:' + str(self._id) + ', statistics:' + str(self.statistics) + \
            '}'


class Statistics:

    """
{'scenarios': [{'volume': 36, 'tests': [{'index': 0, 'server': {'rx': {'bytes': 11885276.0, 'msgs': 95849}, 'tx': {'bytes': 30671680.0, 'msgs': 95849}}, 'instances': {'executed': 95834, 'timeouts': 0, 'total': 95870, 'errors': 0}, 'client': {'rx': {'bytes': 30666880.0, 'msgs': 95834}, 'tx': {'bytes': 11886144.0, 'msgs': 95856}}, 'steps': [{'timeouts': 0, 'duration': {'max': 0.003764, 'avg': 0.0024740000000000001, 'min': 0.003264}, 'skipped': 0, 'executed': 95856, 'errors': 0}, {'timeouts': 0, 'duration': {'max': 0.0, 'avg': 0.0, 'min': 0.0}, 'skipped': 0, 'executed': 95856, 'errors': 0}, {'timeouts': 0, 'duration': {'max': 0.0020040000000000001, 'avg': 0.001041, 'min': 0.0}, 'skipped': 0, 'executed': 95849, 'errors': 0}, {'timeouts': 0, 'duration': {'max': 0.0, 'avg': 0.0, 'min': 0.0}, 'skipped': 0, 'executed': 95849, 'errors': 0}, {'timeouts': 0, 'duration': {'max': 0.001874, 'avg': 0.0010300000000000001, 'min': 0.0}, 'skipped': 0, 'executed': 95834, 'errors': 0}], 'asserts': [], 'duration': {'max': 0.013592, 'avg': 0.0042579999999999996, 'min': 0.0033960000000000001}}], 'hosts': [{'rtx': {'rx': {'bytes': 30666880.0, 'msgs': 95834}, 'tx': {'bytes': 11886144.0, 'msgs': 95856}}}, {'rtx': {'rx': {'bytes': 11885276.0, 'msgs': 95849}, 'tx': {'bytes': 30671680.0, 'msgs': 95849}}}], 'name': 0}], 'summary': {'duration': 17.796984999999999, 'instances': {'executed': 95834, 'timeouts': 0, 'total': 95870, 'errors': 0}, 'client': {'rx': {'bytes': 30666880.0, 'msgs': 95834}, 'tx': {'bytes': 11886144.0, 'msgs': 95856}}, 'asserts': {'failed': 0, 'executed': 0, 'errors': 0}, 'server': {'rx': {'bytes': 11885276.0, 'msgs': 95849}, 'tx': {'bytes': 30671680.0, 'msgs': 95849}}}}
"""

    jsonData = None
    scenarios = []
    summary = None
    volume = 0

    def __init__(self, jsonData=None):
        if jsonData == None:
            return
        for key in jsonData:
            if key == 'scenarios':
                self.scenarios = Scenarios(jsonData[key])
                self.volume = self.scenarios.volume
            if key == 'summary':
                self.summary = Summary(jsonData[key])

    def __str__(self):
        return '{ scenarios:' + str(self.scenarios) + ', summary:' + str(self.summary) + \
            '}'


class Scenarios:

    scenarios = []

    volume = [0]

    def __init__(self, scenarioList=None):
        if scenarioList == None:
            return
        for jsonData in scenarioList:
            scenario = Scenario(jsonData)
            (self.volume)[0] = scenario.volume


class Scenario:
    VOLUME = 'volume'
    TESTS = 'tests'
    HOSTS = 'hosts'
    NAME = 'name'
    jsonData = None
    name = ''
    volume = 0
    hosts = []
    tests = []
    scenario = {VOLUME: '', TESTS: '', HOSTS: '', NAME: ''}

    def __init__(self, jsonData=None):
        if jsonData == None:
            return
        for key in jsonData:
            if key == self.NAME:
                self.name = jsonData[key]
            if key == self.VOLUME:
                self.volume = jsonData[key]
            if key == self.HOSTS:
                self.hosts = jsonData[key]

            if key == self.TESTS:
                for itum in jsonData[key]:
                    testStats = TestStats(itum)

    def __str__(self):
        self.jsonData = '{ scenarios:' + self.scenarios + ',summary:' + \
            self.summary + '}'
        return simplejson.dumps(self.jsonData)


class HostStats:
    """
    TODO
    """
    jsonData = None

    def __init__(self, jsonData=None):
        if jsonData == None:
            return
        for key in jsonData:
            log.info('HostStats key ---> %s', key)
            log.info(str(jsonData[key]))


class TestStats:
    jsonData = None
    INDEX = 'index'
    SERVER = 'server'
    INSTANCES = 'instances'
    CLIENT = 'client'
    STEPS = 'steps'
    ASERTS = 'asserts'
    DURATION = "duration"
    index = 0
    server = None
    instances = None
    client = None
    asserts = None
    duration = None
    steps = []

    def __init__(self, jsonData=None):
        if jsonData == None:
            return
        for key in jsonData:
            if key == self.INDEX:
                self.index = jsonData[key]
            elif key == self.SERVER:
                self.server = jsonData[key]
            elif key == self.INSTANCES:
                self.instances = jsonData[key]
            elif key == self.CLIENT:
                self.client = jsonData[key]
            elif key == self.ASERTS:
                self.asserts = jsonData[key]
            elif key == self.DURATION:
                self.duration = jsonData[key]
            elif key == self.STEPS:
                self.steps = jsonData[key]
            else:
                log.warn("UNKNOWN KEY IN TestStats")


class Summary:
    DURATION = "duration"
    INSTANCES = 'instances'
    CLIENT = 'client'
    SERVER = 'server'
    ASSERTS = 'asserts'

    jsonData = None

    jsonData = None
    duration = None
    instances = None
    client = None
    asserts = None
    server = None

    def __init__(self, jsonData=None):
        if jsonData == None:
            return
        for key in jsonData:
            if key == self.DURATION:
                self.duration = jsonData[self.DURATION]
            if key == self.INSTANCES:
                self.instances = jsonData[self.INSTANCES]
            if key == self.CLIENT:
                self.client = jsonData[self.CLIENT]
            if key == self.SERVER:
                self.server = jsonData[self.SERVER]
            if key == self.ASSERTS:
                self.asserts = jsonData[self.ASSERTS]

    def __str__(self):
        self.jsonData = {self.DURATION: self.duration, self.INSTANCES: self.instances,
                         self.CLIENT: self.client, self.SERVER: self.server,
                         self.ASSERTS: self.asserts}
        return simplejson.dumps(self.jsonData)


